home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
filesyst
/
ext2
/
quotache.new
/
quotache
/
quotacheck.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-19
|
20KB
|
725 lines
/*
* QUOTA An implementation of the diskquota system for the LINUX operating
* system. QUOTA is implemented using the BSD systemcall interface
* as the means of communication with the user level. Should work for
* all filesystems because of integration into the VFS layer of the
* operating system. This is based on the Melbourne quota system wich
* uses both user and group quota files.
*
* Program to check disk quotas.
*
* Authors:
* Disk reading routines: Edvard Tuinder <ed@ow.org>
* Quota storing routines: Marco van Wieringen <mvw@mcs.ow.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
/* NOTE: This is a PRE-ALPHA version !
This version of quotacheck is based on the `hints' of Remy Card and has
*NOT* been fully tested. It *SHOULD* not be able to destroy any data,
but *ANYTHING* may happen.
Comments please to ed@ow.org.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/stat.h>
#include <stdio.h>
#include <linux/quota.h>
#include <stdarg.h>
#include <sys/file.h>
#include <mntent.h>
#include <getopt.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <e2fsck.h>
#define DEF_BLOCKSIZE 1024
#define NODQUOT (struct dquot *)NULL
#ifndef lint
static char RCS_checkquota[] = "$Id: quotacheck.c,v 2.6 1995/02/18 19:57:20 ed Exp root $";
#endif
struct dquot {
int dq_id; /* id this applies to (uid, gid) */
struct dqblk dq_dqb; /* diskquota for id */
struct dquot *next; /* pointer to next id */
};
struct dlinks {
ino_t ino;
size_t size;
size_t blksize;
u_long id;
struct dlinks *next;
};
struct dirs {
char *dir_name;
struct dirs *next;
};
void add_to_quota(struct stat *, int);
void dump_to_file(char *, char *, int);
void store_dlinks(struct stat *, int);
void remove_list(void);
void scan_dir(char *);
void scan_fs(char *);
void add_to_quota_ext2(int, long, int);
void abort_now(const char *, const char *, ...);
void add_dlinks(u_long *, u_long *, int, int);
static char bits[] = "|/-\\";
#define BITS_SIZE 4 /* sizeof(bits) == 5 */
dev_t cur_dev;
char dflag = 0, vflag = 0;
char check_usr, check_grp;
long files_done, dirs_done;
u_long highestid[MAXQUOTAS];
char *quotatypes[] = INITQFNAMES;
char log_buf[16384]; /* for dflag stderr buffering */
#ifdef DEBUG_MALLOC
static size_t malloc_mem = 0;
static size_t free_mem = 0;
#endif
struct dquot *dquot_list[MAXQUOTAS] = {NODQUOT, NODQUOT};
struct dquot *mru_dquot[MAXQUOTAS] = {NODQUOT, NODQUOT};
struct dlinks *stored_links[MAXQUOTAS] = {(struct dlinks *)NULL, (struct dlinks *)NULL};
/*
* This simple algorithm calculates the size of a file in blocks.
* It is not perfect but works most of the time.
*/
static inline size_t isize_to_blocks(size_t isize, size_t blksize)
{
u_long blocks;
u_long indirect;
if (!blksize)
blksize = DEF_BLOCKSIZE;
blocks = (isize / blksize) + ((isize % blksize) ? 1 : 0);
if (blocks > 10) {
indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */
if (blocks > (10 + 256)) {
indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */
if (blocks > (10 + 256 + (256 << 8)))
indirect++; /* triple indirect blocks */
}
blocks += indirect;
}
return blocks;
}
/*
* Ok check each memory allocation.
*/
void *xmalloc(size_t size)
{
void *ptr;
#ifdef DEBUG_MALLOC
malloc_mem += size;
#endif
ptr = malloc(size);
if (ptr == (void *)NULL)
abort_now("xmalloc", "Virtual memory exhausted\n");
memset(ptr, 0, size);
return(ptr);
}
/*
* Do a lookup of a type of quota for a specific id. Use short cut with
* most recently used dquot struct pointer.
*/
static inline struct dquot *lookup_dquot(int id, int type)
{
register struct dquot *lptr = NODQUOT;
/*
* First fast lookup when same as used before.
*/
if (mru_dquot[type] != NODQUOT && mru_dquot[type]->dq_id == id)
return (mru_dquot[type]);
for (lptr = dquot_list[type]; lptr != NODQUOT; lptr = lptr->next)
if (lptr->dq_id == id) {
mru_dquot[type] = lptr;
return (lptr);
}
return(NODQUOT);
}
/*
* Add a new dquot for a new id to the list.
*/
static inline struct dquot *add_dquot(int id, int type)
{
register struct dquot *lptr;
if (dflag)
fprintf(stderr, "Adding dquot structure type %s for %d\n",
quotatypes[type], id);
lptr = (struct dquot *)xmalloc(sizeof(struct dquot));
lptr->dq_id = id;
lptr->next = dquot_list[type];
dquot_list[type] = lptr;
lptr->dq_btime = lptr->dq_itime = (time_t) 0;
if (id > highestid[type])
highestid[type] = id;
return(lptr);
}
/*
* Check the programs arguments for a specific target.
*/
int oneof(char *target, char *list[], int cnt)
{
register int i;
for (i = 0; i < cnt; i++)
if (strcmp(target, list[i]) == 0)
return (i);
return(-1);
}
/*
* Show a blitting cursor as means of visual progress indicator.
*/
static void blit()
{
static short bitc = 0;
putc(bits[bitc], stdout);
putc('\b', stdout);
bitc++;
bitc %= BITS_SIZE;
}
void usage()
{
fputs("Usage:\n\tquotacheck [-g] [-u] [-vd] -a\n", stderr);
fputs("\tquotacheck [-g] [-u] [-vd] filesys ...\n", stderr);
exit(1);
}
int main(int argc, char **argv)
{
FILE *fp;
int cnt, ch;
struct stat st;
char aflag = 0, gflag = 0, uflag = 0;
long argnum, done;
char *usr_qfnp, *grp_qfnp;
register struct mntent *mnt;
while ((ch = getopt(argc, argv, "avugd")) != EOF) {
switch (ch) {
case 'a':
aflag++;
break;
case 'g':
gflag++;
break;
case 'u':
uflag++;
break;
case 'd':
dflag++;
setbuf(stderr, log_buf);
break;
case 'v':
vflag++;
setbuf(stdout, (char *)NULL);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (vflag && dflag)
vflag = 0;
if (!uflag && !gflag)
uflag++;
if (!aflag && argc == 0)
usage();
fp = setmntent(MNTTAB, "r");
while ((mnt = getmntent(fp)) != (struct mntent *) NULL) {
check_usr = check_grp = 0;
if (argc && ((argnum = oneof(mnt->mnt_dir, argv, argc)) >= 0) ||
((argnum = oneof(mnt->mnt_fsname, argv, argc)) >= 0)) {
done |= 1 << argnum;
} else
if (!aflag || hasmntopt(mnt, MNTOPT_NOAUTO) ||
hasmntopt(mnt, MNTOPT_NOQUOTA))
continue;
if (gflag && hasquota(mnt, GRPQUOTA, &grp_qfnp))
check_grp++;
if (uflag && hasquota(mnt, USRQUOTA, &usr_qfnp))
check_usr++;
if (check_usr || check_grp) {
if ((lstat(mnt->mnt_dir, &st)) == -1) {
fprintf(stderr, "%s: not found\n", mnt->mnt_dir);
perror("lstat");
continue;
}
if (vflag)
fprintf(stdout,"Scanning %s [%s] ", mnt->mnt_fsname, mnt->mnt_dir);
if (S_ISDIR(st.st_mode)) {
cur_dev = st.st_dev;
files_done = dirs_done = 0;
if (check_usr)
add_to_quota(&st, USRQUOTA);
if (check_grp)
add_to_quota(&st, GRPQUOTA);
if (strcmp(mnt->mnt_type, MNTTYPE_EXT2)) {
scan_dir(mnt->mnt_dir);
} else {
scan_fs(mnt->mnt_fsname);
}
dirs_done++;
if (vflag)
fputs("done\n", stderr);
if (vflag || dflag)
fprintf(stderr, "Checked %d directories and %d files\n",
dirs_done, files_done);
} else {
fprintf(stderr, "%s: not a directory\n", mnt->mnt_dir);
exit(0);
}
if (check_usr)
dump_to_file(mnt->mnt_fsname, usr_qfnp, USRQUOTA);
if (check_grp)
dump_to_file(mnt->mnt_fsname, grp_qfnp, GRPQUOTA);
remove_list();
}
}
endmntent(fp);
for (cnt = 0; cnt < argc; cnt++)
if ((done & (1 << cnt)) == 0)
fprintf(stderr, "%s not found in fstab\n", argv[cnt]);
#ifdef DEBUG_MALLOC
fprintf (stderr, "Allocated %d bytes memory\nFree'd %d bytes\nLost %d bytes\n", malloc_mem,
free_mem, malloc_mem - free_mem);
#endif
exit(0);
}
/*
Scan a certain device by walking thru its inode bitmap. This applies to ext2
*ONLY*.
Please note that this is a preliminary implementation. It is not final,
perfect or whatever. It works. Nuff said.
It needs to be modified though!
*/
void scan_fs(char *device)
{
ext2_filsys fs;
errcode_t error;
int inode_buffer_blocks = 0;
ino_t ino;
struct ext2_inode inode;
ext2_inode_scan scan;
ext2fs_inode_bitmap inode_used_map;
ext2fs_inode_bitmap inode_dir_map;
error = ext2fs_open(device, 0, 0, 0, unix_io_manager, &fs);
if (error) {
fprintf(stderr, "quotacheck: error while opening %s\n", device);
exit (0);
}
error = ext2fs_allocate_inode_bitmap(fs, "in-use inode map", &inode_used_map);
if (error) {
fprintf (stderr, "quotacheck: error while allocating inode file bitmap\n");
exit (0);
}
error = ext2fs_allocate_inode_bitmap(fs, "directory inode map", &inode_dir_map);
if (error) {
fprintf (stderr, "quotacheck: error while allocating inode directory bitmap\n");
exit (0);
}
error = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan);
if (error) {
fprintf(stderr, "quotacheck: error while opening inode scan\n");
exit (0);
}
error = ext2fs_get_next_inode(scan, &ino, &inode);
if (error) {
fprintf(stderr, "quotacheck: error while starting inode scan\n");
exit (0);
}
while (ino) {
if (inode.i_links_count) {
if (dflag)
printf ("%ld\n", ino);
if (vflag)
blit();
if (check_usr)
add_to_quota_ext2(inode.i_uid, inode.i_blocks, USRQUOTA);
if (check_grp)
add_to_quota_ext2(inode.i_gid, inode.i_blocks, GRPQUOTA);
if (S_ISDIR(inode.i_mode))
dirs_done++;
else
files_done++;
}
error = ext2fs_get_next_inode(scan, &ino, &inode);
if (error) {
fprintf (stderr, "Something weird while scanning\n");
exit (0);
}
}
}
/*
* Scan a directory with the readdir systemcall. Stat the files and add the sizes
* of the files to the appropriate quotas. When we find a dir we recursivly call
* ourself to scan that dir.
*/
void scan_dir(char *pathname)
{
struct dirs *dir_stack = {(struct dirs *)NULL};
struct dirs *new_dir;
struct dirent *de;
struct stat st;
DIR *dp;
if ((dp = opendir(pathname)) == (DIR *)NULL)
abort_now("opendir", "\n%s\n", pathname);
chdir(pathname);
while ((de = readdir(dp)) != (struct dirent *)NULL) {
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
continue;
if (vflag)
blit();
if ((lstat(de->d_name, &st)) == -1) {
fprintf(stderr, "Hmm, file `%s/%s' not found\n", pathname, de->d_name);
fputs("Guess you'd better run fsck first !\nexiting...\n", stderr);
perror("lstat");
exit(1);
}
if (check_usr)
add_to_quota(&st, USRQUOTA);
if (check_grp)
add_to_quota(&st, GRPQUOTA);
if (S_ISDIR(st.st_mode)) {
if (st.st_dev != cur_dev)
continue;
/*
* Add this to the directory stack and check this later on.
*/
if (dflag)
fprintf(stderr, "pushd %s/%s\n", pathname, de->d_name);
new_dir = xmalloc(sizeof(struct dirs));
new_dir->dir_name = xmalloc(strlen(pathname) + strlen(de->d_name) + 2);
sprintf(new_dir->dir_name, "%s/%s", pathname, de->d_name);
new_dir->next = dir_stack;
dir_stack = new_dir;
} else {
if (dflag)
fprintf(stderr, "\tAdding %s size %d ino %d links %d\n", de->d_name,
st.st_size, st.st_ino, st.st_nlink);
files_done++;
}
}
closedir(dp);
/*
* Traverse the directory stack, and check it.
*/
if (dflag)
fputs("Scanning stored directories from directory stack\n", stderr);
while (dir_stack != (struct dirs *)NULL) {
new_dir = dir_stack;
dir_stack = dir_stack->next;
if (dflag)
fprintf(stderr, "popd %s\nEntering directory %s\n",
new_dir->dir_name, new_dir->dir_name);
scan_dir(new_dir->dir_name);
dirs_done++;
#ifdef DEBUG_MALLOC
free_mem += sizeof(struct dirs) + strlen(new_dir->dir_name) + 1;
#endif
free(new_dir->dir_name);
free(new_dir);
}
if (dflag)
fprintf(stderr, "Leaving %s\n", pathname);
}
/*
* Store a hardlinked file for later. Add the end we add this to a users
* quota because we don't wanna count it more then ones.
*/
void store_dlinks(struct stat *st, int type)
{
struct dlinks *lptr;
if (dflag)
fprintf(stderr, "Adding hardlink for ino %d\n", st->st_ino);
for (lptr = stored_links[type]; lptr != (struct dlinks *)NULL; lptr = lptr->next)
if (lptr->ino == st->st_ino)
return;
lptr = (struct dlinks *)xmalloc(sizeof(struct dlinks));
if (type == USRQUOTA)
lptr->id = st->st_uid;
else
lptr->id = st->st_gid;
lptr->ino = st->st_ino;
lptr->size = st->st_size;
lptr->blksize = st->st_blksize;
lptr->next = stored_links[type];
stored_links[type] = lptr;
}
/*
* Add a number of blocks and inodes to a quota.
*/
void add_to_quota(struct stat *st, int type)
{
int wanted;
struct dquot *lptr;
switch(type)
{
case USRQUOTA:
wanted = st->st_uid;
break;
case GRPQUOTA:
wanted = st->st_gid;
break;
default:
return;
}
if ((lptr = lookup_dquot(wanted, type)) == NODQUOT)
if ((lptr = add_dquot(wanted, type)) == NODQUOT)
abort_now("add_to_quota", "Can't add dquot structure type %s for uid %d\n",
quotatypes[type], wanted);
/*
* A dir is a special case, we count it for 1 inode and 1 block.
*/
if (S_ISDIR(st->st_mode)) {
lptr->dq_curinodes++;
lptr->dq_curblocks++;
} else {
/*
* A symlink is always counted as 1 inode and 1 block even if the
* filesystem is smart and stuffs it into the inode. Be fair to other
* users that have there files on other filesystems that aren't that smart
* or have symlinks that don't go into the inode (e.g. to long).
*/
if (S_ISLNK(st->st_mode)) {
lptr->dq_curinodes++;
lptr->dq_curblocks++;
} else {
if (st->st_nlink != 1) {
store_dlinks(st, type);
return;
}
lptr->dq_curinodes++;
if (st->st_size)
lptr->dq_curblocks += isize_to_blocks(st->st_size, st->st_blksize);
}
}
}
void abort_now(const char *perror_mes, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fflush(stderr);
perror(perror_mes);
exit(-1);
}
void add_dlinks(u_long * inodes, u_long * blocks, int id, int type)
{
struct dlinks *lptr;
if (dflag)
fprintf(stderr, "Adding blocks from hardlinks for %s %d\n",
quotatypes[type], id);
for (lptr = stored_links[type]; lptr != (struct dlinks *)NULL; lptr = lptr->next) {
if (lptr->id == id) {
(*inodes)++;
if (lptr->size)
*blocks += isize_to_blocks(lptr->size, lptr->blksize);
files_done++;
}
}
}
/*
* Clean up all list from a previous run.
*/
void remove_list()
{
int cnt;
struct dquot *dquot, *dquot_free;
struct dlinks *dlink, *dlink_free;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (dquot_list[cnt] != NODQUOT) {
dquot = dquot_list[cnt];
while (dquot != NODQUOT) {
dquot_free = dquot;
dquot = dquot->next;
#ifdef DEBUG_MALLOC
free_mem += sizeof(struct dquot);
#endif
free(dquot_free);
}
mru_dquot[cnt] = NODQUOT;
}
dquot_list[cnt] = NODQUOT;
if (stored_links[cnt] != (struct dlinks *)NULL) {
dlink = stored_links[cnt];
while (dlink != (struct dlinks *)NULL) {
dlink_free = dlink;
dlink = dlink->next;
#ifdef DEBUG_MALLOC
free_mem += sizeof(struct dlinks);
#endif
free(dlink_free);
}
}
stored_links[cnt] = (struct dlinks *)NULL;
}
}
/*
* Dump the quota info that we have in memory now to the appropriate
* quota file. We lock it during the time we update it.
*/
void dump_to_file(char *fsname, char *quotafile, int type)
{
struct dqblk dq_dqb;
struct dquot *dquot;
int quota_enabled = 0, max_id;
int fd, id = 0;
if (vflag || dflag)
fprintf(stderr, "Using quotafile %s\n", quotafile);
if (quotactl(QCMD(Q_GETQUOTA, type), fsname, 0, (caddr_t)&dq_dqb) == 0)
quota_enabled = 1;
if ((vflag || dflag) && quota_enabled)
fprintf(stderr, "Updating in-core %s quotas\n", quotatypes[type]);
if ((fd = open(quotafile, O_RDWR | O_CREAT, 0600)) < 0)
abort_now("open", "dump_to_file(%s): ", quotafile);
if (flock(fd, LOCK_EX) < 0)
abort_now("flock", "dump_to_file(%s): ", quotafile);
/*
* First dump the gracetimes that are always a first in the
* quotafile. Only dump new gracetimes when creating a new
* quotafile.
*/
if (read(fd, &dq_dqb, sizeof(struct dqblk)) <= 0) {
memset((caddr_t *)&dq_dqb, 0, sizeof(struct dqblk));
dq_dqb.dqb_btime = MAX_DQ_TIME;
dq_dqb.dqb_itime = MAX_IQ_TIME;
write(fd, &dq_dqb, sizeof(struct dqblk));
}
max_id = highestid[type];
while (id <= max_id) {
if (lseek(fd, dqoff(id), SEEK_SET))
read(fd, &dq_dqb, sizeof(struct dqblk));
if ((dquot = lookup_dquot(id, type)) != NODQUOT) {
dq_curinodes = dquot->dq_curinodes;
dq_curblocks = dquot->dq_curblocks;
if (dflag)
fprintf(stderr, "%s %d: curinodes: %d curblocks: %d\n",
quotatypes[type], id, dq_curinodes, dq_curblocks);
add_dlinks(&dq_curinodes, &dq_curblocks, id, type);
if (dflag)
fprintf(stderr, "%s %d: curinodes: %d curblocks: %d\n",
quotatypes[type], id, dq_curinodes, dq_curblocks);
} else
memset((caddr_t *)&dq_dqb, 0, sizeof(struct dqblk));
/*
* If the quota is updated with the systemcall it isn't needed to update
* it in the file. Because the kernel will do that with the next sync.
*/
if (quota_enabled)
if (quotactl(QCMD(Q_SETUSE, type), fsname, id, (caddr_t)&dq_dqb) == 0) {
id++;
continue;
}
if (lseek(fd, dqoff(id), SEEK_SET))
write(fd, &dq_dqb, sizeof(struct dqblk));
id++;
}
flock(fd, LOCK_UN);
close(fd);
}
/*
* Add a number of blocks and inodes to a quota.
*/
void add_to_quota_ext2(int id, long blocks, int type)
{
int wanted;
struct dquot *lptr;
if ((lptr = lookup_dquot(id, type)) == NODQUOT)
if ((lptr = add_dquot(id, type)) == NODQUOT)
abort_now("add_to_quota", "Can't add dquot structure type %s for uid %d\n",
quotatypes[type], id);
lptr->dq_curinodes++;
lptr->dq_curblocks += blocks/2; /* Dunno why /2 but otherwise everthing is wrong */
}